package ngat.oss;

import ngat.message.OSS.*;
import ngat.util.logging.*;
import ngat.phase2.*; 
import ngat.instrument.*;
import ngat.phase2.nonpersist.*;

import java.io.*;
import java.util.*;

import jyd.dbms.*;
import jyd.storable.*;
import jyd.collection.*;

/**
 * Client: RCS.
 * Action: Adds a Group to the Database. 
 * The Source and Configs enclosed within the Observations are also patched in.
 * @author S.N.Fraser
 */

public class ADD_GROUPImpl extends TransactionImpl {
    
    /** The path used to locate the Group in the OSS database. */
    protected Path proposalPath;

    /** The Group to add.*/
    protected Group group;

    /** Map of Observations against Source.*/
    protected Map srcMap;

    /** Map of Observations against InstConfig.*/
    protected Map icMap;

    /** Map of Observations against TeleConfig.*/
    protected Map tcMap;

    /** Create an ADD_GROUPImpl for the specified request.*/
    public ADD_GROUPImpl(ADD_GROUP request) {
	super(request);
	this.proposalPath = request.getProposalPath();
	this.group        = request.getGroup();
	this.srcMap       = request.getSrcMap();
	this.icMap        = request.getIcMap();
	this.tcMap        = request.getTcMap();
    }

    /**
     * Add the Group.
     */
    public TRANSACTION_DONE exec() {
	
	Logger logger = LogManager.getLogger(this);
	
	Transaction t = null;
	try {
	    t = Transaction.current();

	    // Fetch the Proposal.
	    Proposal proposal = null;
	    
	    // This is a serialization or file i/o error.
	    try {
		proposal = OSS_Utilities.fetchProposal(proposalPath);
	    } catch (IOException iox) {
		logger.log(1, CLASS, id, "exec",
			   "Proposal: "+proposalPath.toString()+
			   " Error fetching from Database: "+
			   iox);
		return error(TRANSACTION.DATABASE_READ_ERROR,			   
			     "Proposal: "+proposalPath.toString()+
			     " Error reading from database",
			     iox);
	    }		
	    
	    // If its not there fail.
	    if (proposal == null) {
		logger.log(1, "ADD_GROUP_Impl", id, "exec",
			   "Proposal: "+proposalPath.toString()+" Not in database.");
		return error(TRANSACTION.PATH_RESOLUTION_ERROR, 
			     "Could not find Proposal: "+proposalPath.toString());
	    }
	    
	    // Check if Proposal locked.
	    if (proposal.isLocked() && !(proposal.getLastEditor().equals("RCS"))) {
		//#### maybe this should be an ILLEGAL_DB_ACCESS ?
		
		return error(TRANSACTION.ILLEGAL_DATABASE_ACCESS, 
			     "Proposal ["+proposalPath.toString()+
			     "] is locked at this time by user ["+proposal.getLastEditor()+
			     "]- try again later");
	    }
	    
	    // Check the Group is not already contained in Proposal.
	    if (proposal.findGroup(group.getName()) != null) {
		return error(ADD_GROUP.GROUP_ALREADY_DEFINED,
			     "Proposal ["+proposalPath.toString()+
			     "] already contains Group ["+group.getName());
	    }

	    // Check the (Src, IC, TC) Mappings.
	   
	    boolean      fail     = false;
	    Observation  obs      = null; 
	    StringBuffer failures = new StringBuffer();

	    if (srcMap == null) {
		failures.append(", No Source-Map specified");
		fail = true;
	    }
	    if (icMap == null) {
		failures.append(", No Inst-Config-Map specified");
		fail = true;
	    }
	    if (tcMap == null) { 
		failures.append(", No Tele-Config-Map specified");
		fail = true;
	    }
	    
	    if (!fail) {
		Iterator it = group.listAllObservations();
		while (it.hasNext()) {
		    obs = (Observation)it.next();
		    
		    String srcId = (String)srcMap.get(obs);
		    Source src = proposal.findSource(srcId);
		    if (src == null) {
			failures.append("\nObs: "+obs.getName()+" - Source ("+srcId+") not found.");
			fail = true;
		    }
		    String icId  = (String)icMap.get(obs);
		    InstrumentConfig ic = proposal.findInstrumentConfig(icId);
		    if (ic == null) {
			failures.append("\nObs: "+obs.getName()+" - InstConfig ("+icId+") not found.");
			fail = true;
		    }
		    
		    
		    String tcId  = (String)tcMap.get(obs);
		    TelescopeConfig tc = proposal.findTelescopeConfig(tcId);
		    if (tc == null) {
			failures.append("\nObs: "+obs.getName()+" - TeleConfig ("+tcId+") not found.");
			fail = true;
		    }
		}
	    }
	    
	    if (fail) {
		logger.log(1, "ADD_GROUP_Impl", id, "exec",
			   "Proposal ["+proposalPath.toString()+
			   "] Content mapping error:"+
			   failures.toString());
		
		return error(ADD_GROUP.CONTENT_MAPPING_ERROR, 
			     "Proposal ["+proposalPath.toString()+
			     "] Content mapping error:"+
			     failures.toString());
	    }

	   
	    // Ok to add Group.
	    proposal.addGroup(group);

	    // Add the Srcs/ICs/TCs etc 
	    obs = null;

	    Iterator it = group.listAllObservations();
	    while (it.hasNext()) {
		obs = (Observation)it.next();
		
		String srcId = (String)srcMap.get(obs);
		Source src = proposal.findSource(srcId);
		obs.setSource(src);
		String icId  = (String)icMap.get(obs);
		InstrumentConfig ic = proposal.findInstrumentConfig(icId);
		obs.setInstrumentConfig(ic);

		String tcId  = (String)tcMap.get(obs);
		TelescopeConfig tc = proposal.findTelescopeConfig(tcId);
		obs.setTelescopeConfig(tc);
	    }
	    
	    // Calculate the Group's exec times.
	    Scheduling.calculateExecutionTimes(group);

	    if (group instanceof FixedGroup) {
		FixedGroup fg = (FixedGroup)group;
		Scheduling.modifyFixedGroup(fg);
		logger.log(1, "ADD_GROUPImpl", id, "exec",
			   "Adding fixed group: "+fg.getName()+
			   " at "+sdf.format(new Date(fg.getFixedTime())));
	    }

	    Scheduling.updateMetricsCache(group);
	    logger.log(1, "ADD_GROUPImpl", id, "exec",
		       "Added group max elevation in night to cache");
	    
	    // Save the Proposal now.
	    try {
		OSS_Utilities.storeProposal(proposal);
	    } catch (IOException ix) {
		logger.log(1, "ADD_GROUPImpl", id, "exec",
			   "Proposal ["+proposalPath.toString()+
			   "] Unable to Store: "+ix);
		return error(TRANSACTION.DATABASE_UPDATE_ERROR, 
			     "Proposal ["+proposalPath.toString()+
			     "] Unable to Store",
			     ix);
	    }

	    ADD_GROUP_DONE done = new ADD_GROUP_DONE(id);
	    done.setSuccessful(true);
	    return done;
		
	} finally { 
	    t.commit();
	}
    }
    
}


